package modelo.dao;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;

import util.Pair;
import util.Utilidades;

import modelo.DetalleSolicitud;
import modelo.Empleado;
import modelo.Equipo;
import modelo.EstadoServicio;
import modelo.Impuesto;
import modelo.Material;
import modelo.MaterialAsignado;
import modelo.Servicio;
import modelo.ServicioPrestado;
import modelo.Status;
import modelo.Tarea;
import modelo.TareaAsignada;
import modelo.TipoServicio;

public class DAOServiciosPrestados {
	protected HashMap<Integer, ServicioPrestado> serviciosPrestados = new HashMap<Integer, ServicioPrestado>();
	protected HashMap<Integer, TareaAsignada> tareasAsignadas = new HashMap<Integer, TareaAsignada>();
	protected HashMap<Pair<Integer, Integer>, MaterialAsignado> materialesAsignados = new HashMap<Pair<Integer, Integer>, MaterialAsignado>();
	
	public void cargarServiciosPrestados() {
		ResultSet rs = Servidor.consultar(
			"SELECT *\n" +
			"FROM servicio_prestado\n"
			);
		
		try {
			while (rs.next()) {
				ServicioPrestado e = new ServicioPrestado(
						rs.getInt("cod_servicio_prestado"),
						rs.getString("descripcion"),
						rs.getString("comentario"),
						rs.getDate("fecha_inicio"),
						rs.getDate("fecha_finalizacion"),
						rs.getDouble("costo"),
						Servidor.equipos().consultarEquipoPorCodigo(rs.getInt("cod_equipo")),
						Servidor.solicitudes().consultarDetalleSolicitudPorCodigo(rs.getInt("cod_detalle")),
						Servidor.servicios().consultarServicioPorCodigo(rs.getInt("cod_servicio")),
						Servidor.servicios().consultarEstadoServicioPorCodigo(rs.getInt("cod_estado_servicio")),
						Servidor.empleados().consultarEmpleadoPorCodigo(rs.getString("cod_jefe_proyecto")));
				serviciosPrestados.put(e.clave(), e);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public ServicioPrestado agregarServicioPrestado(
			int codigo, String descripcion, String comentarios,
			Date fechaInicio, Date fechaFinalizacion,
			double costo, Equipo equipo, DetalleSolicitud detalle,
			Servicio servicio, EstadoServicio estadoServicio,
			Empleado jefe) {

		boolean b = Servidor.ejecutar(
				"INSERT INTO servicio_prestado(\n" +
				"    cod_servicio_prestado, descripcion, comentario, fecha_inicio,\n" +
				"    fecha_finalizacion, costo, cod_equipo, cod_detalle, cod_servicio,\n" + 
				"    cod_estado_servicio, cod_jefe_proyecto)\n" +
				"VALUES\n" +
				"    (" + codigo + ", '" + descripcion + "', '" + comentarios
				+ "', " + Utilidades.convertirFechaParaSQL(fechaInicio) + ", " + Utilidades.convertirFechaParaSQL(fechaFinalizacion)
				+ ", " + costo + ", " + equipo.getCodigo() + ", " + detalle.getCodigo() + ", " + servicio.getCodigo()
				+ ", " + estadoServicio.getCodigo() + ", '" + jefe.getCedula() + "')"
				);
			
		if (!b) return null;
		
		ServicioPrestado e = new ServicioPrestado(codigo, descripcion, comentarios, fechaInicio, fechaFinalizacion, costo, equipo, detalle, servicio, estadoServicio, jefe);
		serviciosPrestados.put(e.clave(), e);
		
		return e;
	}
	
	public ArrayList<ServicioPrestado> consultarServiciosPrestados() {
		ResultSet rs = Servidor.consultar(
			"SELECT cod_servicio_prestado\n" +
			"  FROM servicio_prestado\n"
			);
		
		return adjuntarServicioPrestado(rs);
	}
	
	public ArrayList<ServicioPrestado> consultarServiciosPrestadosPorFiltro(
			EstadoServicio estadoServicio, Empleado jefeProyecto,
			Date desdeFechaInicio, Date hastaFechaInicio,
			Date desdeFechaFin, Date hastaFechaFin
			) {
		
		String sql = "SELECT cod_servicio_prestado\n" +
					 "  FROM servicio_prestado\n" +
					 " WHERE TRUE\n";
		
		if (estadoServicio != null)
			sql = sql + "   AND cod_estado_servicio = " + estadoServicio.getCodigo() + "\n";
		
		if (jefeProyecto != null)
			sql = sql + "   AND cod_jefe_proyecto = '" + jefeProyecto.getCedula() + "'\n";
		
		if (desdeFechaInicio != null && hastaFechaInicio != null)
			sql = sql + "   AND fecha_inicio BETWEEN " + Utilidades.convertirFechaParaSQL(desdeFechaInicio) + " AND " + Utilidades.convertirFechaParaSQL(hastaFechaInicio) + "\n";
		
		if (desdeFechaFin != null && hastaFechaFin != null)
			sql = sql + "   AND fecha_finalizacion BETWEEN " + Utilidades.convertirFechaParaSQL(desdeFechaFin) + " AND " + Utilidades.convertirFechaParaSQL(hastaFechaFin) + "\n";
		
		ResultSet rs = Servidor.consultar(sql);
		
		return adjuntarServicioPrestado(rs);
	}
	
	public ServicioPrestado consultarServicioPrestadoPorCodigo(int cod) {
		return serviciosPrestados.get(cod);
	}
	
	public int nuevoCodigoServicioPrestado() {
		try {
			ResultSet rs = Servidor.consultar(
					"SELECT nextval('servicio_prestado_cod_servicio_prestado_seq'::regclass) codigo\n"
					);
			if (rs.next()) return rs.getInt("codigo");
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return -1;
	}

	private ArrayList<ServicioPrestado> adjuntarServicioPrestado(ResultSet rs) {
		ArrayList<ServicioPrestado> lst = new ArrayList<ServicioPrestado>();
		
		try {
			while (rs.next())
				lst.add(serviciosPrestados.get(rs.getInt("cod_servicio_prestado")));
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return lst;
	}
	
	public void cargarTareasAsignadas() {
		ResultSet rs = Servidor.consultar(
			"SELECT *\n" +
			"FROM tarea_asignada\n"
			);
		
		try {
			while (rs.next()) {
				TareaAsignada e = new TareaAsignada(
						rs.getInt("cod_tarea_asignada"),
						rs.getString("descripcion"),
						rs.getString("comentarios"),
						rs.getDate("fecha"),
						rs.getDouble("costo"),
						rs.getString("estado"),
						Servidor.serviciosPrestados().consultarServicioPrestadoPorCodigo(rs.getInt("cod_servicio_prestado")),
						Servidor.tareas().consultarTareaPorCodigo(rs.getInt("cod_tarea")),
						Servidor.empleados().consultarEmpleadoPorCodigo(rs.getString("cedula_empleado")),
						Status.valueOf(rs.getInt("status")));
				tareasAsignadas.put(e.clave(), e);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public ArrayList<TareaAsignada> consultarTareasAsignadas() {
		ResultSet rs = Servidor.consultar(
			"SELECT cod_tarea_asignada\n" +
			"FROM tarea_asignada\n"
			);
		
		return adjuntarTareaAsignada(rs);
	}
	
	public TareaAsignada consultarTareaAsignadaPorCodigo(int cod) {
		return tareasAsignadas.get(cod);
	}

	private ArrayList<TareaAsignada> adjuntarTareaAsignada(ResultSet rs) {
		ArrayList<TareaAsignada> lst = new ArrayList<TareaAsignada>();
		
		try {
			while (rs.next())
				lst.add(tareasAsignadas.get(rs.getInt("cod_tarea_asignada")));
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return lst;
	}
	
	public void cargarMaterialesAsignados() {
		ResultSet rs = Servidor.consultar(
			"SELECT *\n" +
			"FROM material_asignado\n"
			);
		
		try {
			while (rs.next()) {
				MaterialAsignado e = new MaterialAsignado(
						consultarServicioPrestadoPorCodigo(rs.getInt("cod_servicio_prestado")),
						Servidor.materiales().consultarMaterialPorCodigo(rs.getInt("cod_material")),
						rs.getDouble("cantidad"),
						rs.getDouble("monto"));
				materialesAsignados.put(e.clave(), e);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public ArrayList<MaterialAsignado> consultarMaterialesAsignadas() {
		ResultSet rs = Servidor.consultar(
			"SELECT cod_material, cod_servicio_prestado\n" +
			"FROM material_asignado\n"
			);
		
		return adjuntarMaterialAsignada(rs);
	}
	
	public MaterialAsignado consultarMaterialAsignadaPorCodigo(int cod) {
		return materialesAsignados.get(cod);
	}

	private ArrayList<MaterialAsignado> adjuntarMaterialAsignada(ResultSet rs) {
		ArrayList<MaterialAsignado> lst = new ArrayList<MaterialAsignado>();
		
		try {
			while (rs.next())
				lst.add(materialesAsignados.get(new Pair<Integer, Integer>(rs.getInt("cod_material"), rs.getInt("cod_servicio_prestado"))));
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return lst;
	}

	public int nuevoCodigoTareaAsignada() {
		try {
			ResultSet rs = Servidor.consultar(
					"SELECT nextval('tarea_asignada_cod_tarea_asignada_seq'::regclass) codigo\n"
					);
			if (rs.next()) return rs.getInt("codigo");
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return -1;
	}

	public TareaAsignada agregarTareaAsignada(int codigo, String descripcion,
			String comentarios, Date fecha, double costo, String estado,
			ServicioPrestado servicioPrestado, Tarea tareaBase,
			Empleado empleado, Status status) {
		
		boolean b = Servidor.ejecutar(
				"INSERT INTO tarea_asignada(\n" +
				"            descripcion,\n" +
				"            comentarios,\n" +
				"            fecha,\n" +
				"            costo,\n" +
				"            estado,\n" +
				"            status,\n" +
				"            cod_tarea,\n" +
				"            cedula_empleado,\n" +
				"            cod_servicio_prestado,\n" +
				"            cod_tarea_asignada)\n" +
				"     VALUES ('" + descripcion + "',\n'" +
						    	   comentarios + "',\n" +
						           Utilidades.convertirFechaParaSQL(fecha) + ",\n" +
							       costo + ",\n'" +
							       estado + "',\n" +
							       status.toInt() + ",\n" +
							       (tareaBase == null ? "NULL" : tareaBase.getCodigo()) + ",\n" +
							       (empleado == null ? "NULL" : "'" + empleado.getCedula() + "'") + ",\n" +
							       servicioPrestado.getCodigo() + ",\n" +
							       codigo + ")"
				);
			
		if (!b) return null;
		
		TareaAsignada e = new TareaAsignada(codigo, descripcion,
				comentarios, fecha, costo, estado,
				servicioPrestado, tareaBase,
				empleado, status);
		tareasAsignadas.put(e.clave(), e);
		
		return e;
	}

	public MaterialAsignado agregarMaterialAsignado(ServicioPrestado servicioPrestado, Material material,
			double cantidad, double monto) {
		
		boolean b = Servidor.ejecutar(
				"INSERT INTO material_asignado(\n" +
				"            cod_servicio,\n" +
				"            cod_material,\n" +
				"            cantidad,\n" +
				"            monto)\n" +
				"     VALUES (" + servicioPrestado.getCodigo() + ",\n" +
						    	  material.getCodigo() + ",\n" +
						          cantidad + ",\n" +
						          monto + ")"
				);
			
		if (!b) return null;
		
		MaterialAsignado e = new MaterialAsignado(
				servicioPrestado,
				material,
				cantidad,
				monto);
		materialesAsignados.put(e.clave(), e);
		
		return e;
	}
	
	public ArrayList<Pair<EstadoServicio, Integer>> consultarCantidadEstadoServicio() {
		try {
			ArrayList<Pair<EstadoServicio, Integer>> arr = new ArrayList<Pair<EstadoServicio,Integer>>();
			
			ResultSet rs = Servidor.consultar(
					"  SELECT cod_estado_servicio, COUNT() contador\n" +
					"    FROM servicio_prestado\n" +
					"GROUP BY cod_estado_servicio"
					);
		
			while (rs.next()) {
				arr.add(new Pair<EstadoServicio, Integer>(Servidor.servicios().consultarEstadoServicioPorCodigo(rs.getInt("cod_estado_servicio")), rs.getInt("contador")));
			}

			return arr;
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}

	public ArrayList<Pair<TipoServicio, Integer>> consultarCantidadTipoServicio(Date desdeFechaFin, Date hastaFechaFin) {
		try {
			ArrayList<Pair<TipoServicio, Integer>> arr = new ArrayList<Pair<TipoServicio,Integer>>();

			String sql = "    SELECT s.cod_tipo_servicio cod_tipo_servicio, COUNT(*) contador\n" +
						 "      FROM servicio_prestado sp\n" +
						 "INNER JOIN servicio s ON sp.cod_servicio = s.cod_servicio\n" +
						 "     WHERE TRUE\n";
			
			if (desdeFechaFin != null && hastaFechaFin != null)
				sql = sql + "       AND sp.fecha_finalizacion BETWEEN " + Utilidades.convertirFechaParaSQL(desdeFechaFin) + " AND " + Utilidades.convertirFechaParaSQL(hastaFechaFin) + "\n";
			
			sql = sql +  "  GROUP BY s.cod_tipo_servicio";

			ResultSet rs = Servidor.consultar(sql);
		
			while (rs.next()) {
				arr.add(new Pair<TipoServicio, Integer>(Servidor.servicios().consultarTipoServicioPorCodigo(rs.getInt("cod_tipo_servicio")), rs.getInt("contador")));
			}

			return arr;
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}
}
